<html>
    <head>
        <title>静态 C# API 客户端代理</title>
    </head>
    <body>
        <script>
            //  静态C# API 客户端代理

                    /*
                            ABP可以创建C#API客户端代理代码来调用您的远程HTTP服务（REST API）。

                            这样，您就不需要处理HttpClient和其他低级细节来调用远程服务并获得结果。


                            静态C#客户端代理会自动为你处理以下内容：

                                    ①、通过考虑HTTP方法、路由、查询字符串参数、请求有效负载和其他细节，将C#方法调用映射到远程服务器HTTP调用。

                                    ②、通过将访问令牌添"Access Token"加到HTTP标头(Http-Headers)来验证HTTP客户端。

                                    ③、参数对象序列化、反序列化。

                                    ④、处理HTTP API版本控制。

                                    ⑤、将相关id、当前租户id和当前区域性添加到请求中。

                                    ⑥、正确地处理服务器发送的错误消息并抛出正确的异常。

                            任何类型的.net 客户端都可以使用此系统来使用你的Http Api
                    */

                    /*
                        一、 静态客户端代理 与 动态客户端代理

                                    ABP提供了两种类型的客户端代理生成系统。

                                    本文档解释了静态客户端代理，它在您的开发时间时成客户端代码。

                                    您还可以查看动态C#API客户端代理留档以了解如何使用运行时生成的代理。

                                    开发时（静态）客户端代理生成具有性能优势，因为它不需要在运行时获取HTTP API定义。

                                    但是，每当您更改API端点定义时，您应该重新生成客户端代理代码。
                                    
                                    另一方面，动态客户端代理是在运行时生成的，它提供了更简单的开发体验。
                    */


                    /*
                        二、 服务接口

                                    您的服务/控制器应该实现一个在服务器和客户端之间共享的接口。

                                    因此，首先在Application.Contracts项目中定义一个服务接口，如果您已经使用启动模板创建了解决方案。

                                    eg：

                                        public interface IBookAppService : IApplicationService
                                        {
                                            Task<List<BookDto>> GetListAsync();
                                        }

                                    备注：

                                        你的应用服务接口应该实现  IRemmoteService 接口 ，这样ABP就知道为此服务接口创建自动api控制器，并且为其创建客户端代理。

                                        由于IApplicationService继承了IRemoteService接口，上面的IBookAppService满足这个条件。

                                    在您的服务应用程序中实现此类。您可以使用autoAPI控制器系统将服务公开为REST API端点。
                    */



                    /*
                        三、有契约(Contracts) 和 无契约(Contracts)      With Contracts or Without Contracts

                                1、不包含契约定义 (Without Contracts)

                                            静态代理项目中包含与Application.Contracts项目中相同的 类、枚举、其他..的定义（Dto\枚举）。

                                            因此它想使用这些类型的的时候，必须从外部引用程序引用Application.Contracts项目。

                                            然而，当我们想创建完全独立开发和部署的微服务时（不想依赖Application.Contracts项目），这就成了一个问题。

                                            我们希望使用静态代理生成，即使不依赖于目标服务的application.contracts包。


                                2、包含契约定义 (With Contracts)

                                            在客户端（包括应用服务接口）生成所有classes/enums/other类型，这也是generate-proxy命令的默认行为.
                     */



                     /*
                        四、客户端代理生成   Client Proxy Generation


                                1、首先，将"Volo. Abp.Http.Client" nuget包添加到您的客户端项目中：(启动模板已经包含了此包)
                                
                                        "Install-Package Volo.Abp.Http.Client"

                                2、然后将AbpHttpClientModule依赖项添加到您的模块中：

                                                        [DependsOn(typeof(AbpHttpClientModule))] // 添加此模块依赖  (启动模板已经包含了此模块依赖)
                                                        public class MyClientAppModule : AbpModule
                                                        {
                                                        }


                                                    现在，它已准备好为静态客户端代理生成配置应用程序。


                                                    (1)、带有契约(contracts)定义示例:

                                                                [DependsOn(
                                                                    typeof(AbpHttpClientModule), //used to create client proxies
                                                                    typeof(AbpVirtualFileSystemModule) //virtual file system
                                                                )]
                                                                public class MyClientAppModule : AbpModule
                                                                {
                                                                    public override void ConfigureServices(ServiceConfigurationContext context)
                                                                    {
                                                                        //  添加生成静态客户端代理配置
                                                                        context.Services.AddStaticHttpClientProxies(
                                                                            typeof(MyClientAppModule).Assembly  // 当前客户端代理程序集包含契约的定义 (类、枚举、其他...)
                                                                        );

                                                                        // 将生成的 app-generate-proxy.json 文件包含到虚拟文件系统中
                                                                        Configure<AbpVirtualFileSystemOptions>(options =>
                                                                        {
                                                                            options.FileSets.AddEmbedded<MyClientAppModule>();
                                                                        });
                                                                    }
                                                                }


                                                        (2)、不带契约(contracts)定义示例:

                                                                [DependsOn(
                                                                    typeof(AbpHttpClientModule), //used to create client proxies
                                                                    typeof(AbpVirtualFileSystemModule), //virtual file system
                                                                    typeof(BookStoreApplicationContractsModule) // 依赖BookStoreApplicationContract模块的契约定义模块
                                                                )]
                                                                public class MyClientAppModule : AbpModule
                                                                {
                                                                    public override void ConfigureServices(ServiceConfigurationContext context)
                                                                    {
                                                                        // 添加生成静态客户端代理配置
                                                                        context.Services.AddStaticHttpClientProxies(
                                                                            typeof(BookStoreApplicationContractsModule).Assembly  // 服务契约来自从外部引用的程序集 (类、枚举、其他...)
                                                                        );

                                                                        // 将生成的 app-generate-proxy.json 文件包含到虚拟文件系统中
                                                                        Configure<AbpVirtualFileSystemOptions>(options =>
                                                                        {
                                                                            options.FileSets.AddEmbedded<MyClientAppModule>();
                                                                        });
                                                                    }
                                                                }


                                                    "AddStaticHttpClientProxies"方法获取一个程序集，查找给定程序集中的所有服务接口，并为静态客户端代理生成做准备。



                                                    备注：
                                                        应用程序启动模板在HttpApi.Client项目中为动态客户端代理生成预先配置。

                                                        如果要切换到静态客户端代理，请在HttpApi.Client项目的模块类中将context.Services.AddHttpClientProxies更改为context.Services.AddStaticHttpClientProxies。
                                                    
                                
                            
                                3、配置服务端点(端口)    在Configuration系统中配置服务端口

                                                    在 appsettings.json 文件中的 "RemoteServices"节点部分默认用于配置远程服务的端口。
                                                    
                                                    最简单的配置如下：

                                                                {
                                                                    "RemoteServices": {
                                                                        "Default": {
                                                                        "BaseUrl": "http://localhost:53929/"
                                                                        } 
                                                                    } 
                                                                }

                                                    有关详细配置，请参阅下面的AbpRemoteServiceOptions部分。


                                4、代码生成  

                                                    生成客户端代理代码时，服务器端必须启动并运行。    

                                                    因此，请在BaseUrl上运行为HTTP API提供服务的应用程序，该应用程序的配置如Endpoint Configuration部分中所述。

                                                    在客户端项目（.csproj）的根文件夹中打开命令行终端并键入以下命令：

                                                            (1)、With Contracts 包含契约定义：

                                                                           "abp generate-proxy -t csharp -u http://localhost:53929/"
                                                                           

                                                                    此命令应在ClientProxies文件夹下生成以下文件：

                                                                            ①、BookClientProxy.Generated.cs是本例中实际生成的代理类。
                                                                                
                                                                                        BookClientProxy是一个partial类*，您可以在其中编写自定义代码（ABP不会覆盖它）。

                                                                            ②、IBookAppService.cs是应用服务(接口)。

                                                                            ③、BookDto.cs是应用服务使用的Dto类。

                                                                            ④、app-generate-proxy.json包含有关远程HTTP端点的信息，以此文件作为依据，abp可以正确执行HTTP请求。
                                                                            
                                                                                        此文件必须配置为项目中的嵌入式资源，以便虚拟文件系统可以找到它。

                                                                    
                                                            (2)、Without Contracts 不包含契约定义：

                                                                            "abp generate-proxy -t csharp -u http://localhost:53929/ --without-contracts"

                                                                        此命令应在ClientProxies文件夹下生成以下文件：

                                                                              ①、BookClientProxy.Generated.cs 是本例中实际生成的代理类。

                                                                                            BookClientProxy是一个partial类，您可以在其中编写自定义代码（ABP不会覆盖它）。

                                                                              ②、app-generate-proxy.json包含有关远程HTTP端点的信息，以此文件作为依据，ABP可以正确执行HTTP请求。

                                                                                            此文件必须配置为项目中的嵌入式资源，以便虚拟文件系统可以找到它。


                                                        备注：

                                                                generate-proxy命令仅为您在应用程序中定义的API生成代理。

                                                                如果您正在开发模块化应用程序，您可以指定-m（或--module）参数来指定要生成代理的模块。

                                                                有关其他选项，请参阅ABP CLI留档中的Generate-proxy部分：https://abp.io/docs/latest/cli
                                                                
                     */

                     /*
                         五、使用方法  Usage

                                    使用客户端代理很简单。只需在客户端应用程序代码中注入服务接口：

                                                public class MyService : ITransientDependency
                                                {
                                                    private readonly IBookAppService _bookService;

                                                    public MyService(IBookAppService bookService)
                                                    {
                                                        _bookService = bookService;
                                                    }

                                                    public async Task DoItAsync()
                                                    {
                                                        var books = await _bookService.GetListAsync();
                                                        foreach (var book in books)
                                                        {
                                                            Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
                                                        }
                                                    }
                                                }

                                    此示例注入上面定义的IBookAppService服务接口。每当客户端调用服务方法时，静态客户端代理实现都会进行HTTP调用。
                     */

                     /*
                          六、配置  配置生成静态代理  Configuration


                                    1、AbpRemoteServiceOptions 选项类

                                                AbpRemoteServiceOptions默认情况下从appsettings.json自动设置。

                                                或者，您可以在模块的ConfigureServices方法中配置它以设置或覆盖它。

                                                eg:
                                                        public override void ConfigureServices(ServiceConfigurationContext context)
                                                        {
                                                            context.Services.Configure<AbpRemoteServiceOptions>(options =>
                                                            {
                                                                options.RemoteServices.Default =
                                                                    new RemoteServiceConfiguration("http://localhost:53929/");  // 配置远程服务端口(端点)
                                                            });
                                                            
                                                            //...
                                                        }

                                    2、配置多个远程服务端口(端点)   Multiple Remote Service Endpoints

                                                        上面的示例配置了“默认”远程服务端点。

                                                        您可能对不同的服务有不同的端点（就像在微服务方法中，每个微服务都有不同的端点）。

                                                        在这种情况下，您可以将其他端点添加到配置文件中：

                                                                    {
                                                                        "RemoteServices": {
                                                                            "Default": {
                                                                                "BaseUrl": "http://localhost:53929/"
                                                                            },
                                                                            "BookStore": {
                                                                                "BaseUrl": "http://localhost:48392/"
                                                                            } 
                                                                        } 
                                                                    }
                                                                        
                                                        AddStaticHttpClientProxies方法可以获取远程服务名称的附加参数。
                                                        
                                                        示例：
                                                            context.Services.AddStaticHttpClientProxies(
                                                                typeof(BookStoreApplicationContractsModule).Assembly,
                                                                remoteServiceConfigurationName: "BookStore"
                                                            ); 
                                                            
                                                            

                                                    ★、远程服务配置提供程序   Remote Service Configuration Provider

                                                                在某些情况下，您可能需要获取特定远程服务的远程服务配置。

                                                                为此，您可以使用IRemoteServiceConfigurationProvider接口。

                                                                eg：获取“BookStore”远程服务的远程服务配置
                                                                        
                                                                        public class MyService : ITransientDependency
                                                                        {
                                                                            private readonly IRemoteServiceConfigurationProvider _remoteServiceConfigurationProvider;

                                                                            public MyService(IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider)
                                                                            {
                                                                                _remoteServiceConfigurationProvider = remoteServiceConfigurationProvider; // 注入远程服务配置提供程序服务
                                                                            }

                                                                            public async Task GetRemoteServiceConfiguration()
                                                                            {
                                                                                var configuration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("BookStore"); // 获取远程服务配置信息
                                                                                Console.WriteLine(configuration.BaseUrl);
                                                                            }
                                                                        }

                                    3、失败重试逻辑 和 Polly 集成

                                                如果要为客户端代理的失败远程HTTP调用添加重试逻辑，
                                                
                                                可以在模块类的PreConfigureServices方法中配置AbpHttpClientBuilderOptions。



                                                eg: 使用Polly库在失败时重试3次

                                                    public override void PreConfigureServices(ServiceConfigurationContext context)
                                                    {
                                                        PreConfigure<AbpHttpClientBuilderOptions>(options =>
                                                        {
                                                            options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
                                                            {
                                                                clientBuilder.AddTransientHttpErrorPolicy(policyBuilder =>
                                                                    policyBuilder.WaitAndRetryAsync(
                                                                        3,
                                                                        i => TimeSpan.FromSeconds(Math.Pow(2, i))
                                                                    )
                                                                );
                                                            });
                                                        });
                                                    }


                                                
                                                此示例使用 "Microsoft. Exents.Http.Polly" 包。
                                                
                                                您还需要导入Polly命名空间（using Polly;）才能使用WaitAndRetryAsync方法。
                     */
        </script>
    </body>
</html>